package com.tkjt.pms.calc.core;

import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.tkjt.pms.calc.IndexCalcDependecyService;
import com.tkjt.pms.calc.common.DepartOwner;
import com.tkjt.pms.calc.common.IndexCalcParallel;
import com.tkjt.pms.calc.common.IndexCalcSerial;
import com.tkjt.pms.calc.common.NodeDepenChains;
import com.tkjt.pms.modules.common.mapper.TkIndexPermissionMapper;
import com.tkjt.pms.persist.dao.IndexCalcDao;
import com.tkjt.pms.persist.mapper.PmsMapper;

@Transactional
@Service
public abstract class IndexCalcDependecyServiceBase implements IndexCalcDependecyService {

	@Autowired
	protected TkIndexPermissionMapper tkIndexPermissionMapper;

	@Qualifier("pmsMapper")
	@Autowired
	protected PmsMapper pmsMapper;

	@Autowired
	@Qualifier("indexCalcDao")
	protected IndexCalcDao indexCalcDao;

	protected IndexCalcSerial buildIndexCalcSerial(String dataType, String indexYearMonth, Set<DepartOwner> dplDepartCodes, Set<String> indexValueCodes) {
		IndexCalcSerial indexCalcSerial = new IndexCalcSerial();
		for (DepartOwner item : dplDepartCodes) {
			for (String ivCode : indexValueCodes) {
				IndexCalcParallel parallel = new IndexCalcParallel();
				parallel.setDataType(dataType);
				parallel.setIndexYearMonth(indexYearMonth);
				parallel.setIndexValueCode(ivCode);
				parallel.setIndexDepart(item.getIndexDepart());
				parallel.setIndexScorer(item.getIndexScorer());
				indexCalcSerial.addParallel(parallel);
			}
		}
		return indexCalcSerial;
	}

	/******************************************************************************************************************
	 * *说明：加载单个数据项计算依赖层级关系
	 * 
	 * @param indexYearMonth
	 * @param indexValueCode
	 * @return
	 */
	protected Map<Integer, Set<String>> loadAndBuildNodeDependencies(String nodeType, String indexYearMonth, String indexGroup, String nodeCode) {

		Set<String> nodeCodes = new HashSet<String>();
		if (!StringUtils.isEmpty(nodeCode)) {
			nodeCodes.add(nodeCode);
		}
		return this.loadAndBuildNodeDependencies(nodeType, indexYearMonth, indexGroup, nodeCodes);

	}

	/******************************************************************************************************************
	 * *说明：加载多个数据项计算依赖层级关系
	 * 
	 * @param indexYearMonth
	 * @param indexValueCode
	 * @return
	 */
	protected Map<Integer, Set<String>> loadAndBuildNodeDependencies(String nodeType, String indexYearMonth, String indexGroup, Set<String> nodeCodes) {

		Map<Integer, Set<String>> nodesLevelChain = new HashMap<Integer, Set<String>>();
		/****************************************************************************
		 * *Step1: 加载多个数据项的层级关系
		 */
		Integer nodeLevel = Integer.valueOf(0);
		for (String nodeCode : nodeCodes) {
			LinkedList<String> tempLevelChain = indexCalcDao.loadNodeLevelChain(nodeType, indexYearMonth, indexGroup, nodeCode);
			for (int index = 0; index < tempLevelChain.size(); index++) {
				Integer currentNodeLevel = index + 1;
				Set<String> levelCodes = nodesLevelChain.get(currentNodeLevel);
				if (null == levelCodes) {
					levelCodes = new HashSet<String>();
					nodesLevelChain.put(currentNodeLevel, levelCodes);
				}
				if (currentNodeLevel > nodeLevel) {
					nodeLevel = currentNodeLevel;
				}
				levelCodes.add(tempLevelChain.get(index));
			}
		}
		/*****************************************************************************
		 * *Step2: 加载Steop1中，层级关系中所有数据项的依赖关系
		 */
		NodeDepenChains nodeDepenChains = null;
		{
			Set<String> depenChainSearchCodes = new HashSet<String>();
			for (int index = 1; index <= nodeLevel; index++) {
				depenChainSearchCodes.addAll(nodesLevelChain.get(Integer.valueOf(index)));
			}
			if (!depenChainSearchCodes.isEmpty()) {
				nodeDepenChains = indexCalcDao.loadNodeDepenChains(nodeType, indexYearMonth, indexGroup, depenChainSearchCodes);
			}
			if (null == nodeDepenChains) {
				return nodesLevelChain;
			}
		}
		/*****************************************************************************
		 * *Step3: 把Step1中的层级关系 与 Step2中的依赖关系，合并成计算层级链
		 */
		Integer startLevel = Integer.valueOf(0);
		while (true) {
			startLevel += 1;
			if (startLevel > nodesLevelChain.size()) {
				break;
			}
			Set<String> oneLevelCodes = new HashSet<>();
			oneLevelCodes.addAll(nodesLevelChain.get(startLevel));
			for (String nodeCalcCode : oneLevelCodes) {
				if (!nodeDepenChains.hasDepenChain(nodeCalcCode)) {
					// 若该数据项(nodeCalcCode)不存在计算依赖关系(该数据项不是其它数据项计算的前置条件)
					continue;
				}
				Integer depenLevel = nodeDepenChains.getDepenChainLevel(nodeCalcCode);
				/**
				 * 节点依赖关系的层级数 小于 节点所在层级 直接将依赖关系的节点插入对应层级集合中 节点不做位移操作
				 */
				if (depenLevel < startLevel) {
					// 若startLevel长度大于calcChain长度，
					for (int dlIndex = 1; dlIndex <= depenLevel; dlIndex++) {
						Set<String> tempDepenNodeCodes = new HashSet<>(nodeDepenChains.getDepenChainLevelCodes(nodeCalcCode, dlIndex));
						nodesLevelChain.get(Integer.valueOf(dlIndex)).addAll(tempDepenNodeCodes);
					}
				} else {
					/**
					 * 节点依赖关系的层级数 大于等于 节点所在层级
					 * 节点不能只做位移操作，会影响下一层级和节点的层级关系
					 * 在节点层级和上一层级之间新添n个层级， n = 当前节点依赖关系层级数 - 当前节点层级 + 1 ，用于存放节点依赖关系 超出的层级节点数据
					 * 将当前节点所在层级及所有下级层级都想后移动 n 位
					 */
					int diff = depenLevel - startLevel + 1;
					Integer tempMaxLevel = nodesLevelChain.size();
					for (int maxIndex = tempMaxLevel; maxIndex >= startLevel; maxIndex--) {
						Set<String> moveSet = nodesLevelChain.remove(maxIndex);
						nodesLevelChain.put(Integer.valueOf(maxIndex + diff), moveSet);
					}
					// 将依赖关系的节点插入对应层级集合中
					for (int index = 1; index <= depenLevel; index++) {
						Set<String> tempNodeCalcCodes = nodesLevelChain.get(Integer.valueOf(index));
						if (null == tempNodeCalcCodes) {
							tempNodeCalcCodes = new HashSet<String>();
							nodesLevelChain.put(Integer.valueOf(index), tempNodeCalcCodes);
						}
						tempNodeCalcCodes.addAll(nodeDepenChains.getDepenChainLevelCodes(nodeCalcCode, index));
					}
				}
				// 该nodeCalcCode的depenChain已处理，从depenChain中删除
				nodeDepenChains.removeDepenChain(nodeCalcCode);
			}
		}

		return nodesLevelChain;
	}

}
